import requests
import json
import time
import traceback
import re
import os
import sys

def get_qq_music_info(keyword, cookies=None):
    """
    获取QQ音乐搜索结果的歌曲信息
    :param keyword: 搜索关键词
    :param cookies: QQ音乐登录cookie
    :return: 歌曲信息列表
    """
    print(f"正在搜索: {keyword}")
    
    # 使用用户提供的API端点
    timestamp = int(time.time() * 1000)
    url = f'https://c.y.qq.com/soso/fcgi-bin/search_for_qq_cp'
    
    # 更新请求头，模拟最新浏览器
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        'Referer': 'https://y.qq.com/',
        'Origin': 'https://y.qq.com',
        'Accept': '*/*',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
    }
    
    # 如果有cookie，添加到请求头
    if cookies:
        cookie_str = "; ".join([f"{k}={v}" for k, v in cookies.items()])
        headers['Cookie'] = cookie_str
        print("使用登录信息进行搜索")
    else:
        print("以未登录状态进行搜索")
    
    # 使用用户提供的参数
    params = {
        'g_tk': '5381',
        'uin': '0' if cookies is None else cookies.get("uin", "0").replace("o", ""),
        'format': 'json',  # 使用json而不是jsonp，因为Python处理json更方便
        'inCharset': 'utf-8',
        'outCharset': 'utf-8',
        'notice': '0',
        'platform': 'h5',
        'needNewCode': '1',
        'w': keyword,
        'zhidaqu': '1',
        'catZhida': '1',
        't': '0',
        'flag': '1',
        'ie': 'utf-8',
        'sem': '1',
        'aggr': '0',
        'perpage': '20',
        'n': '20',
        'p': '1',
        'remoteplace': 'txt.mqq.all',
        '_': timestamp
    }

    try:
        print("发送请求...")
        response = requests.get(url, headers=headers, params=params, timeout=10)
        print(f"状态码: {response.status_code}")
        
        response.raise_for_status()
        
        # 检查响应内容
        content = response.text
        
        # 如果响应是JSONP格式，需要提取JSON部分
        if content.startswith('callback(') and content.endswith(')'):
            # 提取JSON部分
            json_str = re.search(r'callback\((.*)\)', content, re.DOTALL).group(1)
            data = json.loads(json_str)
        else:
            # 直接解析JSON
            data = json.loads(content)
        
        songs = []
        
        # 检查是否有歌曲数据
        if 'data' in data and 'song' in data['data'] and 'list' in data['data']['song']:
            song_list = data['data']['song']['list']
            print(f"找到 {len(song_list)} 首歌曲")
            
            for item in song_list:
                try:
                    # 根据实际返回的数据结构提取信息
                    song_info = {
                        'name': item.get('songname', '未知歌曲'),
                        'singer': item.get('singer', [{'name': '未知歌手'}])[0]['name'],
                        'album': item.get('albumname', '未知专辑'),
                        'time': item.get('interval', 0),
                        'mid': item.get('songmid', ''),
                        'media_mid': item.get('media_mid', ''),
                        'songid': item.get('songid', 0)
                    }
                    songs.append(song_info)
                except Exception as e:
                    print(f"处理歌曲信息时出错: {e}")
        else:
            print("未找到歌曲数据")
        
        return songs
            
    except requests.exceptions.RequestException as e:
        print(f"请求异常: {e}")
        traceback.print_exc()
        return []
    except Exception as e:
        print(f"获取音乐信息失败: {e}")
        traceback.print_exc()
        return []

def get_song_url(song_mid, cookies=None):
    """
    获取歌曲的播放URL
    :param song_mid: 歌曲的mid
    :param cookies: QQ音乐登录cookie
    :return: 歌曲的播放URL
    """
    print(f"正在获取歌曲播放URL, mid: {song_mid}")
    
    if cookies:
        print("使用登录信息获取歌曲URL")
    else:
        print("以未登录状态获取歌曲URL")
    
    # 方法1: 使用QQ音乐官方API获取vkey
    print("尝试方法1...")
    timestamp = int(time.time() * 1000)
    guid = "126548448"  # 这个值可以是固定的
    
    url = "https://u.y.qq.com/cgi-bin/musicu.fcg"
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        'Referer': 'https://y.qq.com/portal/player.html',
        'Origin': 'https://y.qq.com',
    }
    
    # 构建请求数据
    data = {
        "req": {
            "module": "CDN.SrfCdnDispatchServer",
            "method": "GetCdnDispatch",
            "param": {
                "guid": guid,
                "calltype": 0,
                "userip": ""
            }
        },
        "req_0": {
            "module": "vkey.GetVkeyServer",
            "method": "CgiGetVkey",
            "param": {
                "guid": guid,
                "songmid": [song_mid],
                "songtype": [0],
                "uin": "0",
                "loginflag": 1,
                "platform": "20"
            }
        },
        "comm": {
            "uin": 0,
            "format": "json",
            "ct": 24,
            "cv": 0
        }
    }
    
    try:
        response = requests.post(url, headers=headers, data=json.dumps(data), timeout=10)
        response.raise_for_status()
        
        result = response.json()
        
        # 从响应中提取播放URL
        if "req_0" in result and "data" in result["req_0"]:
            midurlinfo = result["req_0"]["data"].get("midurlinfo", [])
            if midurlinfo and "purl" in midurlinfo[0]:
                purl = midurlinfo[0]["purl"]
                if purl:
                    base_url = result["req_0"]["data"].get("sip", [""])[0]
                    play_url = base_url + purl
                    print(f"方法1获取到播放URL: {play_url}")
                    return play_url
    except Exception as e:
        print(f"方法1获取URL失败: {e}")
    
    # 如果方法1失败，尝试方法2 - 使用另一个API
    print("尝试方法2...")
    try:
        url = f"https://c.y.qq.com/base/fcgi-bin/fcg_music_express_mobile3.fcg"
        params = {
            "format": "json",
            "platform": "yqq",
            "cid": "205361747",
            "songmid": song_mid,
            "filename": f"C400{song_mid}.m4a",
            "guid": guid
        }
        
        response = requests.get(url, headers=headers, params=params, timeout=10)
        response.raise_for_status()
        result = response.json()
        
        if "data" in result and "items" in result["data"] and result["data"]["items"]:
            vkey = result["data"]["items"][0].get("vkey", "")
            if vkey:
                play_url = f"http://dl.stream.qqmusic.qq.com/C400{song_mid}.m4a?vkey={vkey}&guid={guid}&uin=0&fromtag=66"
                print(f"方法2获取到播放URL: {play_url}")
                return play_url
    except Exception as e:
        print(f"方法2获取URL失败: {e}")
    
    # 如果方法2也失败，尝试方法3 - 直接构造URL
    print("尝试方法3...")
    # 这个方法可能不稳定，但有时可以工作
    play_url = f"https://isure.stream.qqmusic.qq.com/C400{song_mid}.m4a?guid={guid}&vkey=&uin=0&fromtag=66"
    print(f"方法3构造的URL: {play_url}")
    return play_url

def download_song(url, song_info):
    """
    下载歌曲
    :param url: 歌曲的URL
    :param song_info: 歌曲信息
    :return: 下载的文件路径
    """
    if not url:
        print("无效的歌曲URL")
        return None
    
    # 创建下载目录
    download_dir = "downloads"
    if not os.path.exists(download_dir):
        os.makedirs(download_dir)
    
    # 构建文件名
    filename = f"{song_info['name']} - {song_info['singer']}.m4a"
    # 替换文件名中的非法字符
    filename = re.sub(r'[\\/*?:"<>|]', "_", filename)
    filepath = os.path.join(download_dir, filename)
    
    print(f"开始下载: {filename}")
    try:
        response = requests.get(url, stream=True, timeout=30)
        response.raise_for_status()
        
        total_size = int(response.headers.get('content-length', 0))
        block_size = 1024  # 1 KB
        downloaded = 0
        
        with open(filepath, 'wb') as file:
            for data in response.iter_content(block_size):
                file.write(data)
                downloaded += len(data)
                # 显示下载进度
                done = int(50 * downloaded / total_size) if total_size > 0 else 0
                sys.stdout.write(f"\r[{'=' * done}{' ' * (50 - done)}] {downloaded}/{total_size} bytes")
                sys.stdout.flush()
        
        print(f"\n下载完成: {filepath}")
        return filepath
    except Exception as e:
        print(f"下载歌曲失败: {e}")
        traceback.print_exc()
        return None